|
Creating COM objects in Macromedia ColdFusion MX can
be significantly slower than in Macromedia ColdFusion 5.
This TechNote explains how you can improve COM
performance through using the Application scope among
all ColdFusion pages.
The Java call to create a new COM object instance can
take substantial time. As a result, creating COM objects
in ColdFusion MX can be significantly slower than in
ColdFusion 5. For example, on some systems, creating a
Microsoft Word application object can take more than one
second using ColdFusion MX, while on the same system,
the overhead of creating the Word object in ColdFusion 5
might be about 200
milliseconds. WorkAround In ColdFusion MX,
you can improve COM performance substantially if you can
share one COM object in the Application scope among all
ColdFusion pages.
Use this technique only if the following are true:
| • |
The COM object does not need to be created
for every request or session. (For
session-specific objects, consider using the
technique described in this TechNote with the
Session scope in place of the Application scope.)
|
| • |
The COM object is designed for
sharing. |
Because the object can be accessed from multiple
pages and sessions simultaneously, you must also
consider the following threading and locking issues:
| • |
For best performance, the object should be
multi-threaded. Otherwise, only one request can
access the object at a time. |
| • |
Lock the code that accesses and modifies
common data. In general, you do not have to lock
code that modifies a shared object's data,
including writable properties or file contents, if
the data (as opposed to the object) is not shared
by multiple requests. However, specific locking
needs depend on the COM object's semantics,
interface, and implementation. |
| • |
All cflock tags in the application that use
an Application scope lock share one lock.
Therefore, code that accesses a frequently-used
COM object inside an Application scope lock can
become a bottleneck and reduce throughput if many
users request pages that use the
object. |
Note: You can also improve
performance of some COM objects by creating Java stubs,
as described in TechNote 22922: Accessing Complex
COM Objects using Java Stubs.
Using a Java stub does not improve performance as
much as sharing the COM object, but the technique works
with all COM objects. To correctly access complex COM
objects that do not properly make all their features
available through the COM IDispatcher interface, you
must generate Java stubs. Therefore, to get the greatest
performance increase and prevent possible problems, use
both techniques. Example 1: Using the
FileSystem object
The following example uses the Microsoft FileSystem
Scripting object in the Application scope. This code
creates a user-defined function that returns a structure
consisting of the drive letters and free disk space for
all hard drives on the system. <cfapplication name="comtest" clientmanagement="No" Sessionmanagement="yes">
<!--- Uncomment the following line if you must delete the object from the Application scope during debugging. Then restore the comments.
This technique is faster than stopping and starting the ColdFusion server. --->
<!--- <cfset structdelete(Application, "fso")> --->
<!--- The getFixedDriveSpace user-defined function returns a structure with the drive letters as keys
and the drive's free space as data for all fixed drives on a system.
The function does not take any arguments --->
<cffunction name="getFixedDriveSpace" returnType="struct" output=True>
<!--- If the FileSystemObject does not exist in the Application scope, create it. --->
<!--- For information on the use of initialization variables and locking in this code, see
“Locking Application variables efficiently” in Chapter 15, “Using Persistent Data and Locking”
of Developing ColdFusion MX Applications with CFML. --->
<cfset fso_is_initialized = False>
<cflock scope="application" type="readonly" timeout="12">
<cfset fso_is_initialized = StructKeyExists(Application, "fso")>
</cflock>
<cfif not fso_is_initialized >
<cflock scope="Application" type="EXCLUSIVE" timeout="12">
<cfif NOT StructKeyExists(Application, "fso")>
<CFOBJECT TYPE="COM" ACTION="create" CLASS="Scripting.FileSystemObject"
NAME="Application.fso" server="\\localhost">
</cfif>
</cflock>
</cfif>
<!--- Get the drives collection and loop through it to populate the structure. --->
<cfset drives=Application.fso.drives()>
<cfset driveSpace=StructNew()>
<cfloop collection="#drives#" item="curDrive">
<!--- A DriveType of 2 indicates a fixed disk --->
<cfif curDrive.DriveType IS 2>
<!--- Use dynamic array notation with the drive letter for the struct key --->
<cfset driveSpace["#curDrive.DriveLetter#"]=curDrive.availablespace>
</cfif>
</cfloop>
<cfreturn driveSpace>
</cffunction>
<!--- Test the function. Get the execution time for running the function --->
<cfset start = getTickCount()>
<cfset DriveInfo=getFixedDriveSpace()>
<cfoutput>Execution Time: #int(getTickCount()-start)# milliseconds</cfoutput><br>
Results: <br>
<cfdump var="#driveInfo#">
Example 2: Using the Microsoft Word
application object
The following example uses the Microsoft Word
application COM object in the Application scope to
convert a word document to HTML. This example works with
Word 97 as written. To work with Word 2000, change the
"Val(10)" to "Val(8). This example uses an Application
scope lock to ensure that no other page interrupts
processing of the file. <cfapplication name="comtest" clientmanagement="No" Sessionmanagement="yes">
<!--- Uncomment the following line if you need to delete the object from the Application scope --->
<!--- <cfset structdelete(Application, "MyWordObj")> --->
<!--- use the GetTickCount function to get a current time indicator, used for displaying
the total processing time. --->
<cfset start = GetTickCount()>
<!--- If necessary, create the Word.application obeject and put it in the application scope --->
<cfset WordObj_is_initialized = False>
<cflock scope="application" type="readonly" timeout=12>
<cfset WordObj_is_initialized = StructKeyExists(application, "MyWordObj")>
</cflock>
<cfif not WordObj_is_initialized >
<cflock scope="Application" type="exclusive" timeout="12">
<cfif not StructKeyExists(application, "MyWordObj")>
<!--- First try to connect to an existing Word object --->
<cftry>
In try. <br>
<cfobject type="com"
action="connect"
class="Word.application"
name="Application.MyWordobj"
context="local">
<cfcatch>
In catch <br>
<!--- There is no existing object, create one --->
<cfobject type="com"
action="Create"
class="Word.application"
name="Application.MyWordobj"
context="local">
</cfcatch>
</cftry>
<cfset Application.mywordobj.visible = False>
</cfif>
</cflock>
</cfif>
<!--- convert a word document in temp.doc to an html file in temp.htm --->
<!--- Because this example uses a fixed file name, multiple pages might try to use the file
simultaneously. The lock ensures that all actions from reading the input file through
closing the output file are a single "atomic" operation, and the next page cannot access
the file until the current page completes all processing.
Use a named lock instead of the Application scope lock to reduce lock contention. --->
<cflock name="WordObjLock" type="exclusive" timeout="12">
<cfset docs = application.mywordobj.documents()>
<cfset docs.open("c:\CFusionMX\wwwroot\temp.doc")>
<cfset converteddoc = application.mywordobj.activedocument>
<!--- Val(10) works with Word 97. Use Val(8) for Word 2000 --->
<cfset converteddoc.saveas("c:\CFusionMX\wwwroot\temp.htm",val(10))>
<cfset converteddoc.close()>
</cflock>
<cfoutput>
Conversion of temp.htm Complete<br>
Execution Time: #int(getTickCount()-start)# milliseconds<br>
</cfoutput>
Note: Your code must do all
necessary cleanup. For example, if your COM object (such
as Word) is in a separate process, you must clean up the
processes that are no longer needed. For example, the
following code quits Word and deletes the MyWordObj from
the Application scope. This issue is particularly
important during development, where you might end up
with multiple copies of a process running. <cfif StructKeyExists(Application, "MyWordobj")>
<cfset Application.MyWordobj.quit()>
<cfset structdelete(Application, "MyWordObj")>
</cfif>
Related TechNotes:
|